﻿namespace NServiceBus.AcceptanceTests.Core.Feature;

using System;
using System.Threading;
using System.Threading.Tasks;
using AcceptanceTesting;
using EndpointTemplates;
using Features;
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;

public class When_depending_on_feature : NServiceBusAcceptanceTest
{
    [Test]
    public async Task Should_start_startup_tasks_in_order_of_dependency()
    {
        var context = await Scenario.Define<Context>()
            .WithEndpoint<EndpointWithFeatures>(b => b.CustomConfig(c =>
            {
                c.EnableFeature<DependencyFeature>();
                c.EnableFeature<TypedDependentFeature>();
            }))
            .Done(c => c.EndpointsStarted)
            .Run();

        using (Assert.EnterMultipleScope())
        {
            Assert.That(context.StartCalled, Is.True);
            Assert.That(context.StopCalled, Is.True);
        }
    }

    class Context : ScenarioContext
    {
        public bool StartCalled { get; set; }
        public bool StopCalled { get; set; }
        public bool InitializeCalled { get; set; }
    }

    public class EndpointWithFeatures : EndpointConfigurationBuilder
    {
        public EndpointWithFeatures() => EndpointSetup<DefaultServer>();
    }

    public class TypedDependentFeature : Feature
    {
        public TypedDependentFeature() => DependsOn<DependencyFeature>();

        protected override void Setup(FeatureConfigurationContext context) => context.RegisterStartupTask<Runner>();

        class Runner(Dependency dependency) : FeatureStartupTask
        {
            protected override Task OnStart(IMessageSession session, CancellationToken cancellationToken = default)
            {
                dependency.Start();
                return Task.CompletedTask;
            }

            protected override Task OnStop(IMessageSession session, CancellationToken cancellationToken = default)
            {
                dependency.Stop();
                return Task.CompletedTask;
            }
        }
    }

    public class DependencyFeature : Feature
    {
        protected override void Setup(FeatureConfigurationContext context)
        {
            context.Services.AddSingleton<Dependency>();
            context.RegisterStartupTask<Runner>();
        }

        class Runner(Dependency dependency) : FeatureStartupTask
        {
            protected override Task OnStart(IMessageSession session, CancellationToken cancellationToken = default)
            {
                dependency.Initialize();
                return Task.CompletedTask;
            }

            protected override Task OnStop(IMessageSession session, CancellationToken cancellationToken = default) => Task.CompletedTask;
        }
    }

    class Dependency(Context context)
    {
        public void Start()
        {
            if (!context.InitializeCalled)
            {
                throw new InvalidOperationException("Not initialized");
            }
            context.StartCalled = true;
        }

        public void Stop()
        {
            if (!context.InitializeCalled)
            {
                throw new InvalidOperationException("Not initialized");
            }

            context.StopCalled = true;
        }

        public void Initialize() => context.InitializeCalled = true;
    }
}